package math.matrix;

import function.unary.UnaryFunction;
import java.util.Arrays;
import list.functions.ListFunctions;

/**
 *
 * Fully Tested: No
 * Fully Documented: No
 * Created: 20-May-2012
 * Last Updated: 20-May-2012
 *
 * @param <T> 
 * @author Shimu Wu
 */
public class Matrix<T> {

    private int offsetX;

    private int offsetY;

    /**
     * The number of rows in the matrix.
     */
    private int maxY;

    /**
     * The number of columns in the matrix.
     */
    private int maxX;

    /**
     * The data in the matrix
     */
    protected T[] data;

    public Matrix(Matrix<T> m) {
        this.set(m.data, m.maxX, m.maxY);
    }

    @SuppressWarnings("unchecked")
    public Matrix(int maxX, int maxY) {
        //this.set(null, maxX, maxY);
        this.set((T[]) new Object[maxX * maxY], maxX, maxY);
    }

    public Matrix(T[] data, int maxX, int maxY) {
        this.set(data, maxX, maxY);
    }

    /**
     * @return the maxY
     */
    public int getMaxY() {
        return maxY;
    }

    /**
     * @return the maxX
     */
    public int getMaxX() {
        return maxX;
    }

    public final T get(int x, int y) {
        return this.data[x + this.getMaxY() * y];
    }

    public final T get(int index) {
        return this.data[index];
    }

    public final void set(int x, int y, T value) {
        this.data[x + this.getMaxY() * y] = value;
    }

    protected final void set(T[] newData, int width, int height) {
        this.data = newData;
        this.maxX = width;
        this.maxY = height;
    }

    public boolean equals(Matrix<T> other) {
        if ((this.getMaxY() == other.getMaxY())
                && (this.getMaxX() == other.getMaxX())) {
            return ListFunctions.equals(Arrays.asList(this.data),
                    Arrays.asList(other.data));
        }
        return false;
    }

    public <E> Matrix<E> map(MatrixMapper<T, E> mapper) {
        Matrix<E> out = new Matrix<E>(getMaxX(), getMaxY());

        for (int x = 0; x < getMaxX(); x++) {
            for (int y = 0; y < getMaxY(); y++) {
                out.set(x, y, mapper.process(x, y, this));
            }
        }
        return out;
    }

    public <E> Matrix<E> map(UnaryFunction<T, E> mapper) {
        Matrix<E> out = new Matrix<E>(getMaxY(), getMaxX());

        for (int x = 0; x < getMaxY(); x++) {
            for (int y = 0; y < getMaxX(); y++) {
                out.set(x, y, mapper.evaluate(this.get(x, y)));
            }
        }
        return out;
    }

    /*
    public void transpose(){
    
    }
    
    public Matrix<T> subMatrix(int offsetX, int offsetY, int width, int height) {
    return new Matrix()
    }*/
    public final void print() {
        for (int r = 0; r < getMaxY(); r++) {
            for (int c = 0; c < getMaxY(); c++) {
                System.out.print(data[c + r * getMaxY()] + ", ");
            }
            System.out.println();
        }
    }
}
